// gcc myCtrol.c -o myCtrol -L/opt/DLAN/lib/ -lixml -lthreadutil -lupnp
#include <stdio.h> 
#include <string.h>
#include <pthread.h>
#include <stdarg.h>
#include "../include/upnp/upnp.h"
#include "../include/upnp/ixml.h"
#include "../include/upnp/upnptools.h"
#include <stdlib.h>
#define DESC_URL_SIZE 200

typedef void (*print_string)(
	/*! [in] Format. */
	const char *string,
	/*! [in] Arguments. */
	...)
#if (__GNUC__ >= 3)
	/* This enables printf like format checking by the compiler */
	__attribute__((format (__printf__, 1, 2)))
#endif
;
pthread_mutex_t display_mutex;
/*******************************************************************************************/
void ixmlNodeList_free(IXML_NodeList *nList);
int SampleUtil_Print(const char *fmt, ...);
char *SampleUtil_GetFirstDocumentItem(IXML_Document *doc, const char *item);
int TvCtrlPointCallbackEventHandler(Upnp_EventType EventType, void *Event, void *Cookie);

/*******************************************************************************************/

int main(int argc,char* argv[]){

  	int ret;
	unsigned short port = 0;
	char *ip_address = NULL;
	UpnpClient_Handle ctrlpt_handle = -1;
	IXML_Document *DescDoc = NULL;
	const char TvDeviceType[] = "urn:schemas-upnp-org:device:tvdevice:1";
  	//  初始化sdk 
  	ret = UpnpInit(NULL,0);
  	if(UPNP_E_SUCCESS != ret)
	{
   		printf("Init uPnP failed!\n");
    		UpnpFinish();
		return ret;
  	}  
	if (!ip_address) {
		ip_address = UpnpGetServerIpAddress();
	}
	if (!port) 
	{
		port = UpnpGetServerPort();
	}

	printf("UPnP Initialized\n"
			 "\tipaddress = %s port = %u\n",
			 ip_address ? ip_address : "{NULL}", port);
  	//  控制点注册
		ret = UpnpRegisterClient(TvCtrlPointCallbackEventHandler,&ctrlpt_handle,&ctrlpt_handle);
		if(UPNP_E_SUCCESS != ret)
		{
    			printf("Error registering CP: %d\n", ret);
			UpnpFinish();
			return ret;
 		}
  	//  搜索
		
		printf("-----------------------------------------------------------------\n");
		ret = UpnpSearchAsync(ctrlpt_handle, 10,TvDeviceType, NULL);
		if (UPNP_E_SUCCESS != ret)
		{
			printf("Error sending search request%d\n", ret);
		}
		sleep(10);
		printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
	while(1);
  	return 0;
}

/**************************************************************************************************************************/
int TvCtrlPointCallbackEventHandler(Upnp_EventType EventType, void *Event, void *Cookie)
{
	char *deviceType = NULL;
	char *friendlyName = NULL;
	char *baseURL = NULL;
	char *relURL = NULL;
	char *udn = NULL;
	switch ( EventType ) {
	case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
	case UPNP_DISCOVERY_SEARCH_RESULT: {
		struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
		IXML_Document *DescDoc = NULL;
		int ret;

		if (d_event->ErrCode != UPNP_E_SUCCESS)
		{
			printf("Error in Discovery Callback -- %d\n",d_event->ErrCode);
		}
		ret = UpnpDownloadXmlDoc(d_event->Location, &DescDoc);
		if (ret != UPNP_E_SUCCESS) 
		{
			printf("Error obtaining device description from %s -- error = %d\n",d_event->Location, ret);
		}
		deviceType = SampleUtil_GetFirstDocumentItem(DescDoc, "deviceType");
		printf("-------------------------------------------%s\n",deviceType);
		friendlyName = SampleUtil_GetFirstDocumentItem(DescDoc, "friendlyName");
		baseURL = SampleUtil_GetFirstDocumentItem(DescDoc, "URLBase");
		relURL = SampleUtil_GetFirstDocumentItem(DescDoc, "presentationURL");
		udn = SampleUtil_GetFirstDocumentItem(DescDoc, "UDN");
		printf("-------------------------------------------%s\n",friendlyName);
		printf("-------------------------------------------%s\n",baseURL);
		printf("-------------------------------------------%s\n",relURL);
		printf("-------------------------------------------%s\n",udn);
		break;
	}
	case UPNP_DISCOVERY_SEARCH_TIMEOUT:
		/* Nothing to do here... */
		break;
	case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: 
		break;
	case UPNP_CONTROL_ACTION_COMPLETE: 
		/* No need for any processing here, just print out results.
		 * Service state table updates are handled by events. */
		break;
	
	case UPNP_CONTROL_GET_VAR_COMPLETE: 
		break;
	/* GENA Stuff */
	case UPNP_EVENT_RECEIVED: 
		break;
	case UPNP_EVENT_SUBSCRIBE_COMPLETE:
	case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
	case UPNP_EVENT_RENEWAL_COMPLETE: 
		break;
	case UPNP_EVENT_AUTORENEWAL_FAILED:
	case UPNP_EVENT_SUBSCRIPTION_EXPIRED: 
		break;
	/* ignore these cases, since this is not a device */
	case UPNP_EVENT_SUBSCRIPTION_REQUEST:
	case UPNP_CONTROL_GET_VAR_REQUEST:
	case UPNP_CONTROL_ACTION_REQUEST:
		break;
	}

	return 0;
}

char *SampleUtil_GetFirstDocumentItem(IXML_Document *doc, const char *item)
{
	IXML_NodeList *nodeList = NULL;
	IXML_Node *textNode = NULL;
	IXML_Node *tmpNode = NULL;
	char *ret = NULL;

	nodeList = ixmlDocument_getElementsByTagName(doc, (char *)item);
	if (nodeList) {
		tmpNode = ixmlNodeList_item(nodeList, 0);
		if (tmpNode) {
			textNode = ixmlNode_getFirstChild(tmpNode);
			if (!textNode) {
				SampleUtil_Print("%s(%d): (BUG) ixmlNode_getFirstChild(tmpNode) returned NULL\n",
					__FILE__, __LINE__); 
				ret = strdup("");
				goto epilogue;
			}
			ret = strdup(ixmlNode_getNodeValue(textNode));
			if (!ret) {
				SampleUtil_Print("%s(%d): ixmlNode_getNodeValue returned NULL\n",
					__FILE__, __LINE__); 
				ret = strdup("");
			}
		} else
			SampleUtil_Print("%s(%d): ixmlNodeList_item(nodeList, 0) returned NULL\n",
				__FILE__, __LINE__);
	} else
		SampleUtil_Print("%s(%d): Error finding %s in XML Node\n",
			__FILE__, __LINE__, item);

epilogue:
	if (nodeList)
		ixmlNodeList_free(nodeList);

	return ret;
}

void ixmlNodeList_free(IXML_NodeList *nList)
{
	IXML_NodeList *next;

	while (nList != NULL) {
		next = nList->next;
		free(nList);
		nList = next;
	}
}

int SampleUtil_Print(const char *fmt, ...)
{
	print_string gPrintFun = NULL;
#define MAX_BUF (8 * 1024)
	va_list ap;
	static char buf[MAX_BUF];
	int rc;

	/* Protect both the display and the static buffer with the mutex */
	pthread_mutex_lock(&display_mutex);

	va_start(ap, fmt);
	rc = vsnprintf(buf, MAX_BUF, fmt, ap);
	va_end(ap);
	if (gPrintFun)
		gPrintFun("%s", buf);

	pthread_mutex_unlock(&display_mutex);

	return rc;
}
